/*!
 * \file Omega.cc
 * \brief Définition des méthodes sur Omega
 * \author CARBONNEL Jessie
 * \version 1.0
 * \date 23 février 2013
 */

#include "Omega.h"

#include <iostream>
#include <vector>
#include <set>
#include <math.h>

using namespace std;
	
Omega::Omega(){}

Omega::Omega(const vector<Variable>& v){
    int n(0);
	for(int i(0);i<(int)v.size();++i){
		variables.push_back(Variable(v[i]));
        n = max(n,v[i].getNumberOfValues());
    }

    if(n<10) coeff = 1;
    else if (n<100) coeff = 10;
    // On suppose que l'on utilisera des variables de moins de 100 valeurs
}

Omega::~Omega(){
	omega.clear();
}

long int Omega::getSize()const{
	int result(1);
	if((int)variables.size() > 0){
		for(int i(0);i<(int)variables.size();++i){
			result = result * variables[i].getNumberOfValues();
		}
	}
	else result = 0;
	return result;
}

int Omega::getCoefficient(){
    return coeff;
}

long int Omega::getActualSize()const{
	return (long int)omega.size();
}

void Omega::eraseOutcome(int o){
	// O(log n)
	omega.erase(o);
}

void Omega::showIntension()const{
	cout<<"Intension : "<<endl;
	for(int i(0);i<(int)variables.size();++i){
		cout<<" ";
		cout<<"v"<<i<<" : "; variables[i].show();
	}
	cout<<endl;
}

int Omega::getNumberOfVariables()const{
    return (int)variables.size();
}

int Omega::getNumberOfValues(int i)const{
    return variables[i].getNumberOfValues();
}

string Omega::getValueOfVariable(int val,int var)const{
    return variables[var].getValue(val);
}

void Omega::showExtension()const{
	set<int>::iterator lit (omega.begin()), lend(omega.end()); 
	for(;lit!=lend;++lit)
	{
        generateOutcome(*lit).show();
	}
}

void Omega::clear(){
	omega.clear();
}

Outcome Omega::generateOutcome(long int id)const{
	Outcome o;
	generateOutcome(id,o);
	return o;
}

void Omega::generateOutcome(long int id, Outcome &o)const{
	int val(0);
	int nov = (int)variables.size();

    for(int i=0;i<nov;i++){
        val = id % (10*coeff);
        id = id / (10*coeff);
		o.addValue(variables[nov-(i+1)].getValue(val));
		val=0;
	}
}

void Omega::generateIndex(){
    int taille(getNumberOfVariables());
    int nov[taille]; // Contient les valeurs max de chaque variables
    int tab[taille]; // Compteur
    int totmax(0);
    int coeff = getCoefficient();

    // On place le nombre de valeurs de chaque variables dans nov[]
    for(int i(0);i<taille;++i){
        nov[i] = getNumberOfValues(i);
        tab[i] = 0;
        totmax = totmax + (nov[i]-1);
    }

    int tot(0);
    int it(taille-1);
    int val;
    omega.insert(0);

   while(tot != totmax){

       // On incrémente le compteur tab[]
        while(tab[it] == (nov[it]-1)){
            tab[it] = 0;
            it = it -1;
        }
        tab[it] = tab[it] + 1;

        // On reinitialise les variables.
        it = taille-1;
        tot = 0;
        val = 0;

        // On calcule le numero de l'Outcome à partir du compteur et on la rajoute à l'ensemble
        for(int i(0);i<taille;++i){
            tot = tot + tab[i];
            val += tab[i] * pow(10*coeff,taille-(i+1));

        }omega.insert(val);
    }
}

int Omega::getCeterisParibus(int o, Preference p, set<int> c){

	int res = -1;
	string mostPreferred = p.getMostPreferred() ;
	string lessPreferred = p.getLessPreferred() ;
    Outcome oc; generateOutcome(o,oc); // L'outcome de départ
	vector<Outcome> constraintRight;

	for(set<int>::iterator it=c.begin(); it!=c.end(); ++it)
	{
		Outcome o1; generateOutcome(*it, o1);
		constraintRight.push_back(o1);	
	} // On genere les outcomes de la partie droite

	vector<Outcome>::iterator lBegin ;

	// Pour chaque Outcome dans la partie droite
	for( lBegin = constraintRight.begin() ; lBegin != constraintRight.end() ; lBegin++ ){

        bool deleteOutcome = true ;

		// Si elle comprend LP et C
		if(p.getCondition() != ""){
            string cond = p.getCondition();
            if( (*lBegin).valide(lessPreferred, cond) )
			{
				vector<string> values = oc.getValues() ;

				// On verifie si toutes les autre valeurs sont les memes entre lBegin et oc
				for( int l = 0 ; l < (int)values.size() ; l++ )
				{
                    if( values[l] != mostPreferred && values[l] != cond )
					{
						if( !((*lBegin).isPresent( values[l] ))  )
						{
							deleteOutcome = false ;
						}
					}
				}
			}
		}
        else{
            // Si elle comprend LP
			if( (*lBegin).valide(lessPreferred) )
			{
				vector<string> values = oc.getValues() ;

				// On verifie si toutes les autre valeurs sont les memes entre lBegin et oc
				for( int l = 0 ; l < (int)values.size() ; l++ )
				{
					if( values[l] != mostPreferred )
					{
						if( !((*lBegin).isPresent( values[l] ))  )
						{
							deleteOutcome = false ;
						}
					}
                }
			}
		}
        if( deleteOutcome ) {

            // w' = lBegin
            res = 0;
            int val;
            int taille( (int)variables.size() );
            for(int i(taille-1);i>=0;i--){
                val = variables[i].isPresent( (*lBegin).getValue(taille-(i+1)) );

                res += val * pow(10,taille-(i+1));
                val=0;
            }
        }

	}
	return res;
}
